#!/bin/python

dic={'int':4,'uint':4,'short':2,'ushort':2,'int[]':4,'short[]':2,'long':8}
dyn={'GameData.Domains.Character.AvatarSystem.AvatarData':'AvatarData_Size'}

def recover(items):
    return '    public '+';\n    public '.join([a[0]+' '+a[1] for a in items])+';'

def size(items):
    res=''
    fix=0
    for i in items:
        if i[0] in dic:
            if i[2]>0:
                fix+=dic[i[0]]*i[2]
            else:
                fix+=dic[i[0]] # record size, int=4, short=2, other=missing
                res+=f'+this.{i[1]}.Length*{dic[i[0]]}'
        elif i[0] in dyn:
            res+=f'+this.{i[1]}.GetSerializedSize()' # if dyn[i[0]] == None else f'+{dyn[i[0]]}'
        else:
            assert 1==0, f"{i[0]}的类型未定义"
    # return 'return (('+str(fix)+res+'+3)>>2)<<2;'
    return 'return '+str(fix)+res+';'

def serfunc(items):
    res=''
    offset=0
    for i in items:
        if i[2]>0:
            (r,o)=ser(i,offset);offset=o;res+=r
    if offset>0:
        res+='ptr+='+str(offset)+';'
    # need_align=True
    for i in items:
        # if i[0]=='int[]' and need_align:
            # res+='ptr=(byte*)(((((int)ptr)+3)/4)*4);'
            # need_align=False
        if i[2]<0:
            res+=serD(i[1],i[0])
    return res+'return ptr;'

def desfunc(items):
    res=''
    offset=0
    for i in items:
        if i[2]>0:
            (r,o)=des(i,offset);offset=o;res+=r
    if offset>0:
        res+='ptr+='+str(offset)+';'
    # need_align=True
    for i in items:
        # if i[0]=='int[]' and need_align:
            # res+='ptr=(byte*)(((((int)ptr)+3)/4)*4);'
            # need_align=False
        if i[2]<0:
            res+=desD(i[1],i[0])
    return res+'return ptr;'

def ser(item,offset):
    if offset+dic[item[0]]*item[2]>=128:
        ret=f'ptr+={offset};'
        offset=0
    else:ret=''
    if item[2]==1:
        z=ser1(item[1],item[0],offset)
    elif item[2]>0:
        z=serF(item[1],item[0][:-2],item[2],offset)
    return (ret+z[0],z[1])

def des(item,offset):
    if offset+dic[item[0]]*item[2]>=128:
        ret=f'ptr+={offset};'
        offset=0
    else:ret=''
    if item[2]==1:
        z=des1(item[1],item[0],offset)
    elif item[2]>0:
        z=desF(item[1],item[0][:-2],item[2],offset)
    return (ret+z[0],z[1])

def ser1(id,type,offset):
    # *(int*)(ptr+offset)=this.id
    val='this.'+id
    ptr='*('+type+'*)'+('ptr' if offset==0 else '(ptr+'+str(offset)+')')
    return '='.join([ptr,val])+';',offset+dic[type]

def des1(id,type,offset):
    # this.id=*(int*)(ptr+offset)
    val='this.'+id
    ptr='*('+type+'*)'+('ptr' if offset==0 else '(ptr+'+str(offset)+')')
    return '='.join([val,ptr])+';',offset+dic[type]

def serF(id,type,len,offset):
    # cptr=&this.id[0]
    val='this.'+id
    ret='fixed('+type+'*fxptr='+val+'){\n            cptr=(byte*)fxptr;'
    cidx=0
    while cidx<len*dic[type]:
        if offset&3 == 0 and len*dic[type]-cidx>=8 :
            # *(long*)(ptr+offset)=*(long*)(cptr+cidx)
            ret+=sd('long','ptr',offset,'cptr',cidx)
            offset+=8
            cidx+=8
        elif offset&3 == 0 and len*dic[type]-cidx>=4 :
            # *(int*)(ptr+offset)=*(int*)(cptr+cidx)
            ret+=sd('int','ptr',offset,'cptr',cidx)
            offset+=4
            cidx+=4
        elif offset&1 == 0 and len*dic[type]-cidx>=2 :
            # *(int*)(ptr+offset)=*(int*)(cptr+cidx)
            ret+=sd('short','ptr',offset,'cptr',cidx)
            offset+=2
            cidx+=2
        elif len*dic[type]-cidx>=1 :
            # *(int*)(ptr+offset)=*(int*)(cptr+cidx)
            assert 2==3, "Should not Reach"
            ret+=sd('byte','ptr',offset,'cptr',cidx)
            offset+=1;
            cidx+=1
    return ret+'}\n        ',offset;

def sd(type,lhs,lof,rhs,rof):
    # *(type*)(lhs+lof)=*(type*)(rhs+rof);
    return '    *('+type+'*)'+(('('+lhs+'+'+str(lof)+')') if lof>0 else lhs) +'=*('+type+'*)'+(('('+rhs+'+'+str(rof)+')') if rof>0 else rhs)+';'

def desF(id,type,len,offset):
    # this.id = new type[len];
    # cptr=&this.id[0]
    val='this.'+id
    # ret='fixed('+type[:-2]+'*fxptr='+val+'){;;cptr=(byte*)fxptr;;'
    ret=val+'= new '+type+'['+str(len)+'];fixed('+type+'*fxptr='+val+'){\n            cptr=(byte*)fxptr;'
    cidx=0
    while cidx<len*dic[type]:
        if offset&3 == 0 and len*dic[type]-cidx>=8 :
            # *(long*)(ptr+offset)=*(long*)(cptr+cidx)
            ret+=sd('long','cptr',cidx,'ptr',offset)
            offset+=8
            cidx+=8
        elif offset&3 == 0 and len*dic[type]-cidx>=4 :
            # *(int*)(ptr+offset)=*(int*)(cptr+cidx)
            ret+=sd('int','cptr',cidx,'ptr',offset)
            offset+=4
            cidx+=4
        elif offset&1 == 0 and len*dic[type]-cidx>=2 :
            # *(int*)(ptr+offset)=*(int*)(cptr+cidx)
            ret+=sd('short','cptr',cidx,'ptr',offset)
            offset+=2
            cidx+=2
        elif len*dic[type]-cidx>=1 :
            # *(int*)(ptr+offset)=*(int*)(cptr+cidx)
            assert 2==3, "Should not Reach"
            ret+=sd('byte','cptr',cidx,'ptr',offset)
            offset+=1;
            cidx+=1
    return ret+'}\n        ',offset;

def serD(id,type):
    # *(short*)ptr=this.id.Length;
    # ptr+=2;
    if type=='int[]':
        ret=f'*(int*)ptr=this.{id}.Length;'
        return ret+f'fixed(int*fxptr=this.{id})ptr=cpyi(ptr+4,(byte*)fxptr,*(int*)ptr,true);'
    elif type=='short[]':
        ret=f'*(short*)ptr=(short)this.{id}.Length;'
        return ret+f'fixed(short*fxptr=this.{id})ptr=cpys(ptr+2,(byte*)fxptr,*(short*)ptr,true);'
    else :
        return f'ptr+=this.{id}.Serialize(ptr);'
def desD(id,type):
    # short count=*(short*)ptr;
    # ptr+=2;
    if type=='int[]':
        ret=f'count=*(int*)ptr;this.{id}=new {type[:-2]}[count];'
        return ret+f'fixed(int*fxptr=this.{id})ptr=cpyi((byte*)fxptr,ptr+4,count,false);'
    elif type=='short[]':
        ret=f'count=*(short*)ptr;this.{id}=new {type[:-2]}[count];'
        return ret+f'fixed(short*fxptr=this.{id})ptr=cpys((byte*)fxptr,ptr+2,count,false);'
    else :
        return f'this.{id}=new();ptr+=this.{id}.Deserialize(ptr);'


for npc_base,nm,cls in [('NpcScan/Aux-CharacterData-common','NpcScan','CharacterData'),('BlackMarket/Aux-CharacterData-common','Neutron3529.BlackMarket','CharacterData')]:
    with open(npc_base+'.CS') as f:a=[l.split('working ''public ')[1] for l in f.read().split('\n') if 'working ''public ' in l]
    a=[[a.split(' ')[0],a.split(' ')[1].split(';')[0],(-1 if '[]' in a or a.split(' ')[0] in dyn else 1) if '=' not in a else int(a.split('=')[1])]  for a in a]
    header='''using System.Collections.Generic;\nusing static Utils.Logger;\nnamespace '''+nm+''';\n\npublic partial class '''+cls+''' {'''
    sizef='    '+('''public unsafe int size(){\n        '''+size(a)).replace(';',';\n        ').strip()+'''\n    }'''
    recimpl=recover(a);
    serimpl='    '+('''public unsafe byte* Ser(byte* ptr){\n        byte* cptr;'''+serfunc(a)).replace(';',';\n        ').strip()+'''\n    }'''
    desimpl='    '+('''public unsafe byte* Des(byte* ptr){\n        byte* cptr;int count;'''+desfunc(a)).replace(';',';\n        ').strip()+'''\n    }'''
    # tail='''    public static Type FixedFieldInfos=typeof(GameData.Domains.Character.Character).GetNestedType("FixedFieldInfos",(BindingFlags)(-1));
    #     public static int AvatarData_Size=FixedFieldInfos?.GetField("Avatar_Id",(BindingFlags)(-1))?.GetValue(null)??76;
    # }'''
    tail='}'
    with open(npc_base+'.python.CS','w') as f:f.write('\n'.join([header,sizef,recimpl,serimpl,desimpl,tail]))
